﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace IBNet.UtilsDataStructuresAndExtensions
{
   public static class ListViewExtensions
   {
      /// <summary>
      /// Executes a specified Action either synchronously or asynchronously on the thread that created this Control.
      /// </summary>
      /// <param name="control">The control</param>
      /// <param name="action">The Action to execute on the thread that created this Control.</param>
      /// <param name="invokeType">The invocation method, either synchronous (Invoke) or asynchronous (BeginInvoke).</param>
      public static void InvokeIfNeeded(this Control control,
         Action action, InvocationMethod invocationMethod)
      {
         if (control.InvokeRequired)
         {
            switch (invocationMethod)
            {
               case InvocationMethod.Invoke:
               {
                  control.Invoke(action);
                  break;
               }
               case InvocationMethod.BeginInvoke:
               {
                  control.BeginInvoke(action);
                  break;
               }
               default:
               {
                  throw new ArgumentException("Invalid InvocationMethod", "invocationMethod");
               }
            }
         }
         else
         {
            action();
         }
      }

      /// <summary>
      /// Determines if this ListView contained a specified key.
      /// </summary>
      /// <param name="listView">This ListView.</param>
      /// <param name="key">The key to locate in this ListView.</param>
      /// <returns>true if </returns>
      public static bool ContainsKey(this ListView listView, string key)
      {
         bool containsKey = false;
         listView.InvokeIfNeeded(delegate
         {
            containsKey = listView.Items.ContainsKey(key);
         }, InvocationMethod.Invoke);
         return containsKey;
      }

      public static void Upsert(this ListView listView, string key, string nameForKey,
         InvocationMethod invocationMethod, params string[] values)
      {
         if (1 + values.Length != listView.Columns.Count)
            throw new ArgumentException("1 + values.Length must be equal to number of columns");

         listView.InvokeIfNeeded(delegate
         {
            if (listView.Items.ContainsKey(key))
            {
               listView.UpdateValues(key, invocationMethod, values);
            }
            else
            {
               listView.Insert(key, nameForKey, invocationMethod, values);
            }
         }, invocationMethod);
      }

      public static void UpdateValues(this ListView listView, string key,
         InvocationMethod invocationMethod, params string[] values)
      {
         if (1 + values.Length != listView.Columns.Count)
            throw new ArgumentException("1 + values.Length must be equal to number of list view columns");

         listView.InvokeIfNeeded(delegate
         {
            ListViewItem.ListViewSubItemCollection subItems = listView.Items[key].SubItems;
            // Asign values to subItems
            for (int i = 0; i != values.Length; ++i)
            {
               // Start at i + 1 because i = 0 is the key, which is not being updated
               subItems[i + 1].Text = values[i];
            }
         }, invocationMethod);
      }

      /// <summary>
      /// Inserts a key and associated value or values into this ListView.
      /// </summary>
      /// <param name="listView">This ListView.</param>
      /// <param name="key">The key to insert.</param>
      /// <param name="displayNameForKey">The display name for the key, which will be displayed in the first column of this ListView.</param>
      /// <param name="invocationMethod">The invocation method.</param>
      /// <param name="values">The values to insert into the columns to the right of column 1 of this ListView. Can be null.</param>
      public static void Insert(this ListView listView, string key,
         string displayNameForKey, InvocationMethod invocationMethod, params string[] values)
      {
         // 1 is to count the length of the key
         if (values != null && 1 + values.Length != listView.Columns.Count)
            throw new ArgumentException("1 + values.Length must be equal to number of list view columns.");

         listView.InvokeIfNeeded(delegate
         {
            ListViewItem newListViewItem = new ListViewItem(displayNameForKey);
            // Name is the key used for listView.ContainsKey()
            newListViewItem.Name = key;
            if (values != null)
            {
               foreach (string value in values)
               {
                  newListViewItem.SubItems.Add(value);
               }
            }
            listView.Items.Add(newListViewItem);
         }, invocationMethod);
      }

      public static void Remove(this ListView listView, string key,
         InvocationMethod invocationMethod)
      {
         listView.InvokeIfNeeded(delegate
         {
            listView.Items.RemoveByKey(key);
         }, invocationMethod);
      }
   }
}
